package org.example.jdbcOperator.dao;

import org.example.jdbcOperator.annotation.Column;
import org.example.jdbcOperator.annotation.Table;
import org.example.jdbcOperator.entity.BaseEntity;
import org.example.jdbcOperator.page.Condition;
import org.example.jdbcOperator.page.Operator;
import org.example.jdbcOperator.page.Page;
import org.example.jdbcOperator.util.DbUtil;
import org.example.jdbcOperator.util.StringUtil;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 基类dao，抽象封装所有的操作
 *
 * @param <T>
 */
public class BaseDao<T> {
    private final DbUtil dbUtil = DbUtil.getInstance();
    private Class<T> tClass;

    private static final int CRUD_ADD = 0;
    private static final int CRUD_UPDATE = 1;
    private static final int CRUD_FIND = 2;
    private static final int CRUD_FIND_List = 3;
    private static final int CRUD_DELETE = 4;

    public BaseDao() {
        Type genericSuperclass = this.getClass().getGenericSuperclass();
        if (genericSuperclass instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass).getActualTypeArguments();
            if (actualTypeArguments != null && actualTypeArguments.length > 0) {
                tClass = (Class<T>) actualTypeArguments[0];
            }
        }
    }

    /**
     * 抽象封装数据添加操作
     */
    public boolean add(T t) {
        String sql = buildSql(CRUD_ADD);
        try {
            Connection connection = dbUtil.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            setPreparedStatement(t, preparedStatement, CRUD_ADD);
            int res = preparedStatement.executeUpdate();
            dbUtil.releaseConnection(connection);
            return res > 0;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 抽象封装数据库更新操作
     */
    public boolean update(T t) {
        String sql = buildSql(CRUD_UPDATE);
        try {
            Connection connection = dbUtil.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            setPreparedStatement(t, preparedStatement, CRUD_UPDATE);
            int res = preparedStatement.executeUpdate();
            dbUtil.releaseConnection(connection);
            return res > 0;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 数据库查询单个实体抽象封装
     */
    public T find(int id) {
        String sql = buildSql(CRUD_FIND);
        T newInstance = null;
        try {
            Connection connection = dbUtil.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1, id);
            System.out.println("preparedStatement sql: " + preparedStatement.toString());
            ResultSet resultSet = preparedStatement.executeQuery();
            if (resultSet.next()) {
                newInstance = setParams(resultSet);
            }
            dbUtil.releaseConnection(connection);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return newInstance;
    }


    public List<T> findList(Page page) {
        String sql = buildSql(CRUD_FIND_List);
        if (!page.getConditions().isEmpty()) {
            // 添加查询条件
            sql += " where " + buildConditionSql(page);
        }
        sql += " limit " + page.getOffset() + "," + page.getPageSize();
        List<T> content = new ArrayList<>();
        try {
            Connection connection = dbUtil.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            setConditionPreparedStatement(preparedStatement, page);
            System.out.println("preparedStatement sql: " + preparedStatement.toString());
            ResultSet resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                T newInstance = setParams(resultSet);
                content.add(newInstance);
            }
            dbUtil.releaseConnection(connection);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return content;
    }

    /**
     * 删除操作封装
     */
    public boolean delete(int... ids) {
        String sql = buildSql(CRUD_DELETE);
        if (ids.length > 1) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < ids.length; i++) {
                sb.append("?");
                sb.append(",");
            }
            sql = sql.replace("?", sb.substring(0, sb.length() - 1));
        }
        try {
            Connection connection = dbUtil.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i < ids.length; i++) {
                preparedStatement.setInt(i + 1, ids[i]);
            }
            System.out.println("preparedStatement sql: " + preparedStatement.toString());
            int res = preparedStatement.executeUpdate();
            dbUtil.releaseConnection(connection);
            return res > 0;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 从数据库获取的数据赋值对象
     */
    private T setParams(ResultSet resultSet) throws SQLException {
        T newInstance;
        try {
            newInstance = tClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        List<Field> fieldList = getEntityFields();
        for (Field field : fieldList) {
            field.setAccessible(true);
            try {
                if (field.isAnnotationPresent(Column.class)) {
                    Column annotation = field.getAnnotation(Column.class);
                    if (annotation.foreignEntity()) {
                        // 如果是自定义对象
                        Blob blob = resultSet.getBlob(annotation.name());
                        ObjectInputStream objectInputStream = new ObjectInputStream(blob.getBinaryStream());
                        field.set(newInstance, objectInputStream.readObject());
                    } else {
                        field.set(newInstance, resultSet.getObject(annotation.name()));
                    }
                } else {
                    field.set(newInstance, resultSet.getObject(StringUtil.convertToUnderline(field.getName())));
                }
            } catch (SQLException | IllegalAccessException | IOException | ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        return newInstance;
    }

    /**
     * 设置数据库链接信息相关值及复制给变量
     */
    private void setPreparedStatement(T t, PreparedStatement preparedStatement, int crudType) {
        List<Field> fieldList = getEntityFields();
        try {
            switch (crudType) {
                case CRUD_ADD: {
                    int ind = 1;
                    for (Field field : fieldList) {
                        field.setAccessible(true);
                        if (field.isAnnotationPresent(Column.class)) {
                            // 注解标注的属性
                            Column annotation = field.getAnnotation(Column.class);
                            if (annotation.primaryKey() && annotation.autoIncrease()) {
                                // 自增主键不用赋值
                                continue;
                            } else if (annotation.autoCreateTime()) {
                                // 创建默认时间赋值
                                preparedStatement.setObject(ind++, new Timestamp(System.currentTimeMillis()));
                            } else {
                                preparedStatement.setObject(ind++, field.get(t));
                            }
                        } else {
                            // 默认取属性名
                            if ("id".equals(StringUtil.convertToUnderline(field.getName()))) {
                                continue;
                            }
                            preparedStatement.setObject(ind++, field.get(t));
                        }
                    }
                    break;
                }
                case CRUD_UPDATE: {
                    int ind = 1;
                    Field whereField = null;
                    for (Field field : fieldList) {
                        field.setAccessible(true);
                        if (field.isAnnotationPresent(Column.class)) {
                            // 注解标注的属性
                            Column annotation = field.getAnnotation(Column.class);
                            if (annotation.primaryKey()) {
                                whereField = field;
                            } else if (!annotation.updateEnable()) {
                                continue;
                            } else {
                                preparedStatement.setObject(ind++, field.get(t));
                            }
                        } else {
                            // 默认取属性名
                            if ("id".equals(StringUtil.convertToUnderline(field.getName()))) {
                                whereField = field;
                            } else {
                                preparedStatement.setObject(ind++, field.get(t));
                            }
                        }
                    }
                    if (whereField != null) {
                        preparedStatement.setObject(ind, whereField.get(t));
                    }
                    break;
                }
            }
        } catch (SQLException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        System.out.println("preparedStatement sql: " + preparedStatement.toString());
    }

    /**
     * 多条件赋值
     */
    private void setConditionPreparedStatement(PreparedStatement preparedStatement, Page page) {
        List<Condition> conditions = page.getConditions();
        int ind = 1;
        for (Condition condition : conditions) {
            try {
                if (Operator.IN == condition.getOperator()) {
                    if (condition.getValue() instanceof List) {
                        List<Integer> list = (List<Integer>) condition.getValue();
                        for (Integer i : list) {
                            preparedStatement.setInt(ind++, i);
                        }
                    } else {
                        throw new RuntimeException("condition sql in param error: " + condition.getValue());
                    }
                } else {
                    preparedStatement.setObject(ind++, condition.getValue());
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 获取类的所有属性，包括baseEntity的所有属性
     */
    private List<Field> getEntityFields() {
        Field[] declaredFields = tClass.getDeclaredFields();
        List<Field> fieldList = new ArrayList<>(Arrays.asList(declaredFields));
        if (BaseEntity.class.isAssignableFrom(tClass)) {
            // 说明继承自BaseEntity，添加基类属性
            Field[] supperDeclaredFields = tClass.getSuperclass().getDeclaredFields();
            fieldList.addAll(Arrays.asList(supperDeclaredFields));
        }
        return fieldList;
    }

    /**
     * 构建sql语句
     */
    private String buildSql(int crudType) {
        String sql = "";
        switch (crudType) {
            case CRUD_ADD:
                sql = "insert into " + getTableName()
                        + " (" + getTableFields() + ")"
                        + " values (" + getAddTableValues() + ");";
                break;
            case CRUD_UPDATE:
                sql = "update " + getTableName()
                        + " set " + getUpdateTableParams();
                break;
            case CRUD_FIND:
                sql = "select " + getTableFields()
                        + " from " + getTableName()
                        + " where " + getTablePrimaryKey() + "=?";
                break;
            case CRUD_FIND_List:
                sql = "select " + getTableFields()
                        + " from " + getTableName();
                break;
            case CRUD_DELETE: {
                sql = "delete from " + getTableName()
                        + " where " + getTablePrimaryKey() + " in (?)";
            }
        }
        System.out.println("build sql: " + sql);
        return sql;
    }

    /**
     * 构建多条件的sql语句
     */
    private String buildConditionSql(Page page) {
        List<String> conditionSqls = new ArrayList<>();
        List<Condition> conditions = page.getConditions();
        for (Condition condition : conditions) {
            String sql = "";
            switch (condition.getOperator()) {
                case GT: {
                    sql = condition.getKey() + ">?";
                    break;
                }
                case GTE: {
                    sql = condition.getKey() + ">=?";
                    break;
                }
                case EQ: {
                    sql = condition.getKey() + "=?";
                    break;
                }
                case LT: {
                    sql = condition.getKey() + "<?";
                    break;
                }
                case LTE: {
                    sql = condition.getKey() + "<=?";
                    break;
                }
                case NEQ: {
                    sql = condition.getKey() + "!=?";
                    break;
                }
                case LIKE: {
                    sql = condition.getKey() + " like ?";
                    break;
                }
                case IN: {
                    if (condition.getValue() instanceof List) {
                        List list = (List) condition.getValue();
                        StringBuilder sb = new StringBuilder();
                        for (int i = 0; i < list.size(); i++) {
                            sb.append("?");
                            sb.append(",");
                        }
                        sql = condition.getKey() + " in (" + sb.substring(0, sb.length() - 1) + ")";
                    } else {
                        throw new RuntimeException("condition sql in param error: " + condition.getValue());
                    }
                    break;
                }
            }
            if (sql.isEmpty()) {
                throw new RuntimeException("condition sql is null: " + condition.getOperator().name());
            }
            conditionSqls.add(sql);
        }
        return String.join(" and ", conditionSqls);
    }

    /**
     * 获得实体对应的数据库表名
     */
    private String getTableName() {
        String tableName;
        if (tClass.isAnnotationPresent(Table.class)) {
            // 如果被注解标注，则使用注解属性
            Table annotation = tClass.getAnnotation(Table.class);
            tableName = annotation.tableName();
            if (!annotation.prefix().isEmpty()) {
                tableName = annotation.prefix() + "_" + tableName;
            }
            if (!annotation.suffix().isEmpty()) {
                tableName = tableName + "_" + annotation.suffix();
            }
        } else {
            tableName = StringUtil.convertToUnderline(tClass.getSimpleName());
        }
        return tableName;
    }

    /**
     * 获取数据库表的所有字段
     */
    private String getTableFields() {
        List<String> res = new ArrayList<>();
        List<Field> fieldList = getEntityFields();
        for (Field field : fieldList) {
            if (field.isAnnotationPresent(Column.class)) {
                // 如果属性被注解标注，则使用注解属性
                res.add(field.getAnnotation(Column.class).name());
            } else {
                res.add(StringUtil.convertToUnderline(field.getName()));
            }
        }
        return String.join(",", res);
    }

    /**
     * 获取添加操作时的字段预填充值
     */
    private String getAddTableValues() {
        List<Field> fieldList = getEntityFields();
        String[] values = new String[fieldList.size()];
        Arrays.fill(values, "?");
        for (int i = 0; i < fieldList.size(); i++) {
            Field field = fieldList.get(i);
            field.setAccessible(true);
            if (field.isAnnotationPresent(Column.class)) {
                // 注解标注的属性
                Column annotation = field.getAnnotation(Column.class);
                if (annotation.primaryKey() && annotation.autoIncrease()) {
                    // 自增主键值为null
                    values[i] = "null";
                }
            } else {
                // 默认取属性名
                if ("id".equals(StringUtil.convertToUnderline(field.getName()))) {
                    values[i] = "null";
                }
            }
        }
        return String.join(",", Arrays.asList(values));
    }

    /**
     * 获取更新操作时的fields和values
     */
    private String getUpdateTableParams() {
        StringBuilder sb = new StringBuilder();
        StringBuilder where = new StringBuilder(" where ");
        List<Field> fieldList = getEntityFields();
        for (Field field : fieldList) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(Column.class)) {
                // 设置了注解字段
                Column annotation = field.getAnnotation(Column.class);
                if (annotation.primaryKey()) {
                    where.append(annotation.name()).append("=?");
                } else if (!annotation.updateEnable()) {
                    continue;
                } else {
                    sb.append(annotation.name()).append("=?,");
                }
            } else {
                // 默认使用字段名
                String fieldName = StringUtil.convertToUnderline(field.getName());
                if ("id".equals(fieldName)) {
                    where.append("id=?");
                } else {
                    sb.append(fieldName).append("=?,");
                }
            }
        }
        if (" where ".contentEquals(where)) {
            throw new RuntimeException("primary key not exist");
        }
        return sb.substring(0, sb.length() - 1) + where;
    }

    /**
     * 获取单个实体字段预填充
     */
    private String getTablePrimaryKey() {
        List<Field> fieldList = getEntityFields();
        String primaryKey = "";
        for (Field field : fieldList) {
            if (field.isAnnotationPresent(Column.class)) {
                Column annotation = field.getAnnotation(Column.class);
                if (annotation.primaryKey()) {
                    return annotation.name();
                }
            } else {
                String name = StringUtil.convertToUnderline(field.getName());
                if ("id".equals(name)) {
                    primaryKey = name;
                }
            }
        }
        if (primaryKey.isEmpty()) {
            throw new RuntimeException("sql primaryKey not exist");
        }
        return primaryKey;
    }
}
